TypeScript generiklari boʻyicha qoʻllanma: sintaksis, afzalliklar va global dasturlashda murakkab maʼlumotlar turlari bilan ishlashning eng yaxshi usullari.
TypeScript Generics: Ishonchli Ilovalar uchun Murakkab Ma'lumot Turlarini O'zlashtirish
JavaScript'ning ustki to'plami bo'lgan TypeScript, dasturchilarga statik tiplash orqali yanada ishonchli va qo'llab-quvvatlanadigan kod yozish imkonini beradi. Uning eng kuchli xususiyatlaridan biri bu generiklar bo'lib, ular sizga turli xil ma'lumotlar turlari bilan ishlay oladigan kod yozishga imkon beradi, shu bilan birga tip xavfsizligini saqlab qoladi. Ushbu qo'llanma TypeScript generiklarini har tomonlama o'rganishni ta'minlaydi, bunda global dasturiy ta'minot ishlab chiqish kontekstida murakkab ma'lumotlar turlariga qo'llanilishiga e'tibor qaratiladi.
Generiklar nima?
Generiklar turli tiplar bilan ishlay oladigan qayta ishlatiladigan kod yozish usulini taqdim etadi. Qo'llab-quvvatlamoqchi bo'lgan har bir tip uchun alohida funksiyalar yoki sinflar yozish o'rniga, siz tip parametrlaridan foydalanadigan bitta funksiya yoki sinf yozishingiz mumkin. Bu tip parametrlari funksiya yoki sinf chaqirilganda yoki yaratilganda ishlatiladigan haqiqiy tiplar uchun joy egallovchilardir. Bu, ayniqsa, murakkab ma'lumotlar tuzilmalari bilan ishlashda foydalidir, chunki bu tuzilmalar ichidagi ma'lumotlar tipi o'zgarishi mumkin.
Generiklardan foydalanishning afzalliklari
- Kodning qayta ishlatilishi: Kodni bir marta yozing va uni turli tiplar bilan ishlating. Bu kodning takrorlanishini kamaytiradi va kodingizni qo'llab-quvvatlashni osonlashtiradi.
- Tip xavfsizligi: Generiklar TypeScript kompilyatoriga kompilyatsiya vaqtida tip xavfsizligini ta'minlash imkonini beradi. Bu ish vaqtida tiplarning mos kelmasligi bilan bog'liq xatoliklarning oldini olishga yordam beradi.
- O'qish qulayligining oshishi: Generiklar sizning funksiyalaringiz va sinflaringiz qaysi tiplar bilan ishlash uchun mo'ljallanganligini aniq ko'rsatib, kodingizni o'qishni osonlashtiradi.
- Ishlash samaradorligining oshishi: Ba'zi hollarda, generiklar ishlash samaradorligini oshirishga olib kelishi mumkin, chunki kompilyator ishlatilayotgan maxsus tiplarga asoslanib yaratilgan kodni optimallashtirishi mumkin.
Generiklarning asosiy sintaksisi
Generiklarning asosiy sintaksisi tip parametrlarini e'lon qilish uchun burchakli qavslardan (< >) foydalanishni o'z ichiga oladi. Bu tip parametrlari odatda T
, K
, V
va hokazo deb nomlanadi, lekin siz har qanday yaroqli identifikatorni ishlatishingiz mumkin. Quyida generik funksiyaning oddiy misoli keltirilgan:
function identity<T>(arg: T): T {
return arg;
}
let myString: string = identity<string>("hello");
let myNumber: number = identity<number>(123);
let myBoolean: boolean = identity<boolean>(true);
console.log(myString); // Output: hello
console.log(myNumber); // Output: 123
console.log(myBoolean); // Output: true
Ushbu misolda <T>
T
nomli tip parametrini e'lon qiladi. identity
funksiyasi T
tipidagi argumentni qabul qiladi va T
tipidagi qiymatni qaytaradi. Funksiyani chaqirishda siz tip parametrini aniq belgilashingiz mumkin (masalan, identity<string>
) yoki TypeScript'ga uni argument tipiga qarab avtomatik aniqlashiga ruxsat berishingiz mumkin.
Murakkab ma'lumotlar turlari bilan ishlash
Generiklar massivlar, obyektlar va interfeyslar kabi murakkab ma'lumotlar turlari bilan ishlashda ayniqsa qimmatli bo'ladi. Keling, ba'zi keng tarqalgan holatlarni ko'rib chiqaylik:
Generik massivlar
Siz generiklardan turli tiplardagi massivlar bilan ishlaydigan funksiyalar yoki sinflar yaratish uchun foydalanishingiz mumkin:
function arrayToString<T>(arr: T[]): string {
return arr.join(", ");
}
let numberArray: number[] = [1, 2, 3, 4, 5];
let stringArray: string[] = ["apple", "banana", "cherry"];
console.log(arrayToString(numberArray)); // Output: 1, 2, 3, 4, 5
console.log(arrayToString(stringArray)); // Output: apple, banana, cherry
Bu yerda arrayToString
funksiyasi T[]
tipidagi massivni qabul qiladi va massivning matnli ko'rinishini qaytaradi. Ushbu funksiya har qanday tipdagi massivlar bilan ishlaydi, bu esa uni yuqori darajada qayta ishlatiladigan qiladi.
Generik obyektlar
Generiklar, shuningdek, turli shakldagi obyektlar bilan ishlaydigan funksiyalar yoki sinflarni aniqlash uchun ham ishlatilishi mumkin:
interface Person {
name: string;
age: number;
country: string; // Added country for global context
}
interface Product {
id: number;
name: string;
price: number;
currency: string; // Added currency for global context
}
function displayInfo<T extends { name: string }>(item: T): void {
console.log(`Name: ${item.name}`);
}
let person: Person = { name: "Alice", age: 30, country: "USA" };
let product: Product = { id: 1, name: "Laptop", price: 1200, currency: "USD" };
displayInfo(person); // Output: Name: Alice
displayInfo(product); // Output: Name: Laptop
Ushbu misolda displayInfo
funksiyasi T
tipidagi obyektni qabul qiladi, u matn tipidagi name
xususiyatiga ega bo'lishi kerak. extends { name: string }
qismi cheklov bo'lib, u T
tip parametri uchun minimal talablarni belgilaydi. Bu funksiyaning name
xususiyatiga xavfsiz murojaat qilishini ta'minlaydi.
Generiklarning ilg'or qo'llanilishi
TypeScript generiklari yanada moslashuvchan va kuchli kod yaratishga imkon beruvchi ilg'or xususiyatlarni taklif qiladi. Keling, ushbu xususiyatlarning ba'zilarini ko'rib chiqaylik:
Bir nechta tip parametrlari
Siz bir nechta tip parametrlari bilan funksiyalar yoki sinflarni aniqlashingiz mumkin:
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
interface Name {
firstName: string;
}
interface Age {
age: number;
}
const person: Name = { firstName: "Bob" };
const details: Age = { age: 42 };
const merged = merge(person, details);
console.log(merged.firstName); // Output: Bob
console.log(merged.age); // Output: 42
merge
funksiyasi T
va U
tipidagi ikkita obyektni qabul qiladi va ikkala obyektning xususiyatlarini o'z ichiga olgan yangi obyektni qaytaradi. Bu turli manbalardan olingan ma'lumotlarni birlashtirishning kuchli usulidir.
Generik cheklovlar
Yuqorida ko'rsatilganidek, cheklovlar generik tip parametri bilan ishlatilishi mumkin bo'lgan tiplarni cheklashga imkon beradi. Bu generik kodning belgilangan tiplar bilan xavfsiz ishlashini ta'minlaydi.
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity([1, 2, 3]); // Output: 3
loggingIdentity("hello"); // Output: 5
// loggingIdentity(123); // Error: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.
loggingIdentity
funksiyasi T
tipidagi argumentni qabul qiladi, u raqam tipidagi length
xususiyatiga ega bo'lishi kerak. Bu funksiyaning length
xususiyatiga xavfsiz murojaat qilishini ta'minlaydi.
Generik sinflar
Generiklar sinflar bilan ham ishlatilishi mumkin:
class DataStorage<T> {
private data: T[] = [];
addItem(item: T) {
this.data.push(item);
}
removeItem(item: T) {
this.data = this.data.filter(d => d !== item);
}
getItems(): T[] {
return [...this.data];
}
}
const textStorage = new DataStorage<string>();
textStorage.addItem("apple");
textStorage.addItem("banana");
textStorage.removeItem("apple");
console.log(textStorage.getItems()); // Output: [ 'banana' ]
const numberStorage = new DataStorage<number>();
numberStorage.addItem(1);
numberStorage.addItem(2);
numberStorage.removeItem(1);
console.log(numberStorage.getItems()); // Output: [ 2 ]
DataStorage
sinfi har qanday T
tipidagi ma'lumotlarni saqlashi mumkin. Bu sizga tip xavfsizligiga ega bo'lgan qayta ishlatiladigan ma'lumotlar tuzilmalarini yaratishga imkon beradi.
Generik interfeyslar
Generik interfeyslar turli tiplar bilan ishlay oladigan shartnomalarni (kontraktlarni) aniqlash uchun foydalidir. Masalan:
interface Result<T, E> {
success: boolean;
data?: T;
error?: E;
}
interface User {
id: number;
username: string;
email: string;
}
interface ErrorMessage {
code: number;
message: string;
}
function fetchUser(id: number): Result<User, ErrorMessage> {
if (id === 1) {
return { success: true, data: { id: 1, username: "john.doe", email: "john.doe@example.com" } };
} else {
return { success: false, error: { code: 404, message: "User not found" } };
}
}
const userResult = fetchUser(1);
if (userResult.success) {
console.log(userResult.data.username);
} else {
console.log(userResult.error.message);
}
Result
interfeysi operatsiya natijasini ifodalash uchun generik tuzilmani aniqlaydi. U T
tipidagi ma'lumotlarni yoki E
tipidagi xatolikni o'z ichiga olishi mumkin. Bu asinxron operatsiyalarni yoki muvaffaqiyatsiz tugashi mumkin bo'lgan operatsiyalarni boshqarish uchun keng tarqalgan usuldir.
Yordamchi tiplar va Generiklar
TypeScript generiklar bilan yaxshi ishlaydigan bir nechta o'rnatilgan yordamchi tiplarni taqdim etadi. Ushbu yordamchi tiplar sizga tiplarni kuchli usullar bilan o'zgartirish va boshqarishga yordam beradi.
Partial<T>
Partial<T>
T
tipining barcha xususiyatlarini ixtiyoriy qiladi:
interface Person {
name: string;
age: number;
}
type PartialPerson = Partial<Person>;
const partialPerson: PartialPerson = { name: "Alice" }; // Valid
Readonly<T>
Readonly<T>
T
tipining barcha xususiyatlarini faqat o'qish uchun (readonly) qiladi:
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly<Person>;
const readonlyPerson: ReadonlyPerson = { name: "Bob", age: 42 };
// readonlyPerson.age = 43; // Error: Cannot assign to 'age' because it is a read-only property.
Pick<T, K>
Pick<T, K>
T
tipidan K
xususiyatlar to'plamini tanlaydi:
interface Person {
name: string;
age: number;
email: string;
}
type NameAndAge = Pick<Person, "name" | "age">;
const nameAndAge: NameAndAge = { name: "Charlie", age: 28 };
Omit<T, K>
Omit<T, K>
T
tipidan K
xususiyatlar to'plamini olib tashlaydi:
interface Person {
name: string;
age: number;
email: string;
}
type PersonWithoutEmail = Omit<Person, "email">;
const personWithoutEmail: PersonWithoutEmail = { name: "David", age: 35 };
Record<K, T>
Record<K, T>
kalitlari K
va qiymatlari T
tipida bo'lgan tip yaratadi:
type CountryCodes = "US" | "CA" | "UK" | "DE" | "FR" | "JP" | "CN" | "IN" | "BR" | "AU"; // Expanded list for global context
type Currency = "USD" | "CAD" | "GBP" | "EUR" | "JPY" | "CNY" | "INR" | "BRL" | "AUD"; // Expanded list for global context
type CurrencyMap = Record<CountryCodes, Currency>;
const currencyMap: CurrencyMap = {
"US": "USD",
"CA": "CAD",
"UK": "GBP",
"DE": "EUR",
"FR": "EUR",
"JP": "JPY",
"CN": "CNY",
"IN": "INR",
"BR": "BRL",
"AU": "AUD",
};
Xaritalangan tiplar (Mapped Types)
Xaritalangan tiplar mavjud tiplarni ularning xususiyatlari bo'yicha takrorlash orqali o'zgartirishga imkon beradi. Bu mavjud tiplar asosida yangi tiplar yaratishning kuchli usulidir. Masalan, siz boshqa tipning barcha xususiyatlarini faqat o'qish uchun (readonly) qiladigan tip yaratishingiz mumkin:
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = {
readonly [K in keyof Person]: Person[K];
};
const readonlyPerson: ReadonlyPerson = { name: "Eve", age: 25 };
// readonlyPerson.age = 26; // Error: Cannot assign to 'age' because it is a read-only property.
Ushbu misolda [K in keyof Person]
Person
interfeysining barcha kalitlari bo'yicha takrorlanadi va Person[K]
har bir xususiyatning tipiga murojaat qiladi. readonly
kalit so'zi har bir xususiyatni faqat o'qish uchun qiladi.
Shartli tiplar (Conditional Types)
Shartli tiplar shartlarga asoslanib tiplarni aniqlashga imkon beradi. Bu turli xil stsenariylarga moslashadigan tiplar yaratishning kuchli usulidir.
type NonNullable<T> = T extends null | undefined ? never : T;
type MaybeString = string | null | undefined;
type StringType = NonNullable<MaybeString>; // string
function getValue<T>(value: T): NonNullable<T> {
if (value == null) { // Handles both null and undefined
throw new Error("Value cannot be null or undefined");
}
return value as NonNullable<T>;
}
try {
const validValue = getValue("hello");
console.log(validValue.toUpperCase()); // Output: HELLO
const invalidValue = getValue(null); // This will throw an error
console.log(invalidValue); // This line will not be reached
} catch (error: any) {
console.error(error.message); // Output: Value cannot be null or undefined
}
Ushbu misolda NonNullable<T>
tipi T
ning null
yoki undefined
ekanligini tekshiradi. Agar shunday bo'lsa, u never
ni qaytaradi, bu esa bu tipga ruxsat etilmaganligini anglatadi. Aks holda, u T
ni qaytaradi. Bu sizga null bo'lmasligi kafolatlangan tiplar yaratishga imkon beradi.
Generiklardan foydalanishning eng yaxshi amaliyotlari
Generiklardan foydalanishda yodda tutish kerak bo'lgan ba'zi eng yaxshi amaliyotlar:
- Tushunarli tip parametr nomlaridan foydalaning: Tip parametrining maqsadini aniq ko'rsatadigan nomlarni tanlang.
- Generik tip parametri bilan ishlatilishi mumkin bo'lgan tiplarni cheklash uchun cheklovlardan foydalaning: Bu sizning generik kodingiz belgilangan tiplar bilan xavfsiz ishlashini ta'minlaydi.
- Generik kodingizni sodda va maqsadli saqlang: Generik kodingizni juda ko'p tip parametrlari yoki murakkab cheklovlar bilan murakkablashtirishdan saqlaning.
- Generik kodingizni to'liq hujjatlashtiring: Tip parametrlarining maqsadini va ishlatiladigan har qanday cheklovlarni tushuntiring.
- Kodning qayta ishlatilishi va tip xavfsizligi o'rtasidagi murosani ko'rib chiqing: Generiklar kodning qayta ishlatilishini yaxshilashi mumkin bo'lsa-da, ular kodingizni murakkablashtirishi ham mumkin. Generiklardan foydalanishdan oldin afzalliklari va kamchiliklarini baholang.
- Mahalliylashtirish va globallashtirishni (l10n va g11n) inobatga oling: Turli mintaqalardagi foydalanuvchilarga ko'rsatilishi kerak bo'lgan ma'lumotlar bilan ishlaganda, generiklaringiz tegishli formatlash va madaniy an'analarni qo'llab-quvvatlashiga ishonch hosil qiling. Masalan, raqam va sana formatlash turli joylarda sezilarli darajada farq qilishi mumkin.
Global kontekstdagi misollar
Keling, generiklarning global kontekstda qanday ishlatilishi mumkinligiga oid ba'zi misollarni ko'rib chiqaylik:
Valyuta konvertatsiyasi
interface ConversionRate {
rate: number;
fromCurrency: string;
toCurrency: string;
}
function convertCurrency<T extends ConversionRate>(amount: number, rate: T): number {
return amount * rate.rate;
}
const usdToEurRate: ConversionRate = { rate: 0.85, fromCurrency: "USD", toCurrency: "EUR" };
const amountInUSD = 100;
const amountInEUR = convertCurrency(amountInUSD, usdToEurRate);
console.log(`${amountInUSD} USD is equal to ${amountInEUR} EUR`); // Output: 100 USD is equal to 85 EUR
Sana formatlash
interface DateFormatOptions {
locale: string;
options: Intl.DateTimeFormatOptions;
}
function formatDate<T extends DateFormatOptions>(date: Date, format: T): string {
return date.toLocaleDateString(format.locale, format.options);
}
const currentDate = new Date();
const usDateFormat: DateFormatOptions = { locale: "en-US", options: { year: 'numeric', month: 'long', day: 'numeric' } };
const germanDateFormat: DateFormatOptions = { locale: "de-DE", options: { year: 'numeric', month: 'long', day: 'numeric' } };
const japaneseDateFormat: DateFormatOptions = { locale: "ja-JP", options: { year: 'numeric', month: 'long', day: 'numeric' } };
console.log("US Date: " + formatDate(currentDate, usDateFormat));
console.log("German Date: " + formatDate(currentDate, germanDateFormat));
console.log("Japanese Date: " + formatDate(currentDate, japaneseDateFormat));
Tarjima xizmati
interface Translation {
[key: string]: string; // Allows for dynamic language keys
}
interface LanguageData<T extends Translation> {
languageCode: string;
translations: T;
}
const englishTranslations: Translation = {
"hello": "Hello",
"goodbye": "Goodbye",
"welcome": "Welcome to our website!"
};
const spanishTranslations: Translation = {
"hello": "Hola",
"goodbye": "Adiós",
"welcome": "¡Bienvenido a nuestro sitio web!"
};
const frenchTranslations: Translation = {
"hello": "Bonjour",
"goodbye": "Au revoir",
"welcome": "Bienvenue sur notre site web !"
};
const languageData: LanguageData<typeof englishTranslations>[] = [
{languageCode: "en", translations: englishTranslations },
{languageCode: "es", translations: spanishTranslations },
{languageCode: "fr", translations: frenchTranslations}
];
function translate<T extends Translation>(key: string, languageCode: string, languageData: LanguageData<T>[]): string {
const lang = languageData.find(lang => lang.languageCode === languageCode);
if (!lang) {
return `Translation for ${key} in ${languageCode} not found.`;
}
return lang.translations[key] || `Translation for ${key} not found.`;
}
console.log(translate("hello", "en", languageData)); // Output: Hello
console.log(translate("hello", "es", languageData)); // Output: Hola
console.log(translate("welcome", "fr", languageData)); // Output: Bienvenue sur notre site web !
console.log(translate("missingKey", "de", languageData)); // Output: Translation for missingKey in de not found.
Xulosa
TypeScript generiklari murakkab ma'lumotlar turlari bilan ishlay oladigan, qayta ishlatiladigan, tip xavfsizligiga ega kod yozish uchun kuchli vositadir. Generiklarning asosiy sintaksisi, ilg'or xususiyatlari va eng yaxshi amaliyotlarini tushunish orqali siz TypeScript ilovalaringizning sifati va qo'llab-quvvatlanishini sezilarli darajada yaxshilashingiz mumkin. Global auditoriya uchun ilovalar ishlab chiqishda generiklar turli xil ma'lumotlar formatlari va madaniy an'analarni boshqarishga yordam beradi, bu esa hamma uchun uzluksiz foydalanuvchi tajribasini ta'minlaydi.